#pragma once
// 线程池的实现
#include <iostream>
#include <queue>
#include <pthread.h>
#include <vector>
#include <string>
#include<unistd.h>
struct ThreadInfo // 保存线程信息的结构体，主要保存tid
{
    pthread_t tid_;          // 线程id
    std::string threadname_; // 线程名字
};
const int defaultThreadNum = 20; // 默认创建的线程数量
template <class T>
class ThreadPool
{
private:
    // 封装锁、条件变量等相关接口
    void Lock()
    {
        pthread_mutex_lock(&mutex_); // 这里会阻塞等待直到获取锁
    }
    void UnLock()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void Wait()
    {
        pthread_cond_wait(&cond_, &mutex_); // 到条件变量的等待队列里等待，同时释放锁
    }
    void WakeUp()
    {
        pthread_cond_signal(&cond_); // 唤醒等待队列里的线程
    }
    bool IsEmpty()
    {
        return tasksquqe_.empty();
    }
    std::string GetThreadName()
    {
        std::string str;
        for (auto &e : threadsinfo_)
        {
            if (e.tid_ == pthread_self())
            {
                str = e.threadname_;
                break;
            }
        }
        return str;
    }

public:
    T Pop()
    {
        T out = tasksquqe_.front();
        tasksquqe_.pop();
        return out;
    }
    static void *Handler(void *arg) // 类内成员函数有一个默认的this指针，加上static后称为静态成员变量没有this指针
    {
        std::cout << "创建线程成功" << std::endl;
        ThreadPool<T> *ptr = static_cast<ThreadPool<T> *>(arg);
        // 线程进来后干嘛？1.获取任务2.执行任务3.得到结果4.结果怎么处理？直接打印-？再转发？
        // 1.从哪里获取任务？->queue任务队列->不单当前线程在获取其他线程也在获取->并发访问共享资源->引发数据不一致问题
        while (true)
        {
            ptr->Lock();           // 解决并发访问带来的数据不一致的问题
            while (ptr->IsEmpty()) // 解决竞争获取锁带来的线程饥饿问题->保证数据安全的前提下让线程有序的访问（同步）
            {
                // 避免伪唤醒进来判断，醒来判断
                // 进来表面临界资源未就绪进入等待队列
                ptr->Wait();
            }
            // 走到这里说明临界资源已就绪，可以开始对资源的操作
            T task = ptr->Pop();
            ptr->UnLock();
            task(); // 执行任务
            std::cout << "i am a thread!!, threadname is: " << ptr->GetThreadName() << " ,i get a task! task is:" << task.GetTask() << " ,task result is:" << task.GetResult() << std::endl;
            sleep(1);
        }
        return nullptr;
    }
    void Push(const T &task)
    {
        Lock();
        tasksquqe_.push(task);
        WakeUp();
        UnLock();
    }
    void Start()
    {
        // 线程池一运行起来就创建好一批线程
        for (int i = 0; i < defaultThreadNum; i++)
        {
            std::cout << "start" << std::endl;
            pthread_create(&threadsinfo_[i].tid_, nullptr, Handler, this);
            std::string str = "thread-";
            str += std::to_string(i + 1);
            threadsinfo_[i].threadname_ = str;
        }
    }
    ~ThreadPool()
    {
        // 析构的时候销毁锁、条件变量
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    static ThreadPool<T> *GetInstance()
    {
        if (tp_ == nullptr)
        {
            // 可能同时有多个线程来获取实例
            pthread_mutex_lock(&lock_);
            tp_ = new ThreadPool<T>();

            pthread_mutex_unlock(&lock_);
        }
        return tp_;
    }

private:
    // 如何设置成单例模式（懒汉模式）？
    ThreadPool(const int &threadnum = defaultThreadNum)
        : threadsinfo_(threadnum)
    {
        // 构造的时候初始化锁、条件变量
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ThreadPool(const T &) = delete;
    ThreadPool<T> &operator=(const T &) = delete;
    static ThreadPool<T> *tp_;
    static pthread_mutex_t lock_; // 全局静态锁不需要初始化和销毁

    std::queue<T> tasksquqe_;             // 任务队列，多线程往这个队列里获取任务，主线程往这个队列里放任务
    std::vector<ThreadInfo> threadsinfo_; // 维护每一个线程的信息（主要用来存放tid）
    pthread_mutex_t mutex_;               // 解决多线程访问资源带来的数据不一致的问题
    pthread_cond_t cond_;                 // 解决多线程竞争锁的能力不一样带来的线程饥饿的问题（让线程有序地访问）
};
// 静态成员变量需要类外定义
template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER; // 设置全局静态锁
